package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"strings"
)

// SubstringInfo 结构体，用于存储子串及其出现次数
type SubstringInfo struct {
	Substring string
	Count     int
}

func substringFrequency(text string, substring string) int {
	count := 0
	index := 0

	for {
		// 在文本中查找子串
		pos := strings.Index(text[index:], substring)
		if pos == -1 {
			// 如果找不到子串，则退出循环
			break
		}
		// 统计子串出现的次数
		count++
		// 更新索引，继续查找下一个子串
		index += pos + len(substring)
	}

	return count
}

func scanDirectory(dir string) {
	// 遍历目录
	err := filepath.WalkDir(dir, func(path string, info os.DirEntry, err error) error {
		if err != nil {
			fmt.Println("访问文件时出错:", err)
			return nil
		}

		if !info.IsDir() && filepath.Ext(path) == ".php" {
			// 打开文件进行读取
			file, err := os.Open(path)
			if err != nil {
				fmt.Println("打开文件时出错:", err)
				return nil
			}

			defer file.Close()
			lineNumber := 0
			total := 0
			// 用于存储每个子串的出现次数
			substringInfos := []SubstringInfo{
				{"GLOB_ONLYDIR", 1},
				{"base64_decode", 1},
				{"str_split", 2},
				{"array_values", 2},
			}
			// 逐行检查
			scanner := bufio.NewScanner(file)
			buf := make([]byte, 0, 64*1024)
			scanner.Buffer(buf, 1024*1024*10)
			for scanner.Scan() {
				line := scanner.Text()
				lineNumber++

				// 遍历子串列表并统计每个子串的出现次数
				for i := range substringInfos {
					frequency := substringFrequency(line, substringInfos[i].Substring)
					if substringInfos[i].Count == frequency {
						total += frequency
						//fmt.Printf("The substring '%s' appears %d times in the given text.\n", substringInfos[i].Substring, frequency)
					}
				}
			}

			if total == 6 {
				//fmt.Printf("次数: %d\n", total)
				fmt.Printf("恶意代码文件: %s\n", path)
			}

			if err := scanner.Err(); err != nil {
				fmt.Println("扫描文件时出错:", err)
			}

		}

		return nil
	})

	if err != nil {
		fmt.Println("目录扫描失败:", err)
	}
}

func main() {
	// 解析命令行参数
	dir := flag.String("dir", ".", "要扫描的目录路径")
	flag.Parse()

	// 调用函数扫描指定目录
	scanDirectory(*dir)
}
